#include <stdio.h>
#include <iostream>
#include <fstream>
#include <vector>
#include <string>
#include <cstdint>
#include <ctime>
#include <cstdlib>
#include <map>
#include <algorithm>
#include <iomanip>
#include <sstream>

#if defined __WIN32__ || defined __CYGWIN__
    #ifdef __GNUC__
        #define SCELMAKER_DLL_EXPORT __attribute__((dllexport))
    #else
        #define SCELMAKER_DLL_EXPORT __declspec(dllexport) // Note: actually gcc seems to also supports this syntax.
    #endif
#else
    #if __GNUC__ >= 4
        #define SCELMAKER_DLL_EXPORT __attribute__((visibility("default")))
    #else
        #define SCELMAKER_DLL_EXPORT
    #endif
#endif

#ifdef __GNUC__
    #define SCELMAKER_DLL_IMPORT 
#else //__WIN32__ || defined __CYGWIN__
    #define SCELMAKER_DLL_IMPORT __declspec(dllimport) // Note: actually gcc seems to also supports this syntax.
#endif

#ifdef SCELMAKER_STATIC
    #define SCELMAKER_API
#elif defined SCELMAKER_EXPORTS
    #define SCELMAKER_API SCELMAKER_DLL_EXPORT
#else
    #define SCELMAKER_API SCELMAKER_DLL_IMPORT
#endif


const std::vector<std::string> &get_pinyin_list(void);
std::vector<uint32_t> CheckSumStream(std::ifstream& file);
uint32_t to_little_endian(uint32_t v);

static std::map<std::string, uint32_t>_py2idx; //pinyin --> index of _pylist
static std::map<char32_t, std::vector<std::string>> _unicode2py; //
static char *_pydata = NULL;
static uint32_t _pydata_size = 0;

static void trimspace(std::string& s) 
{
    const std::string whitespace = " \t\n\r\f\v";
    size_t start = s.find_first_not_of(whitespace);
    if (start == std::string::npos) 
    {
        s.clear();
        return; // 全为空白
    }
    if(start > 0)
    {
        s.erase(0, start);
    }
    size_t end = s.find_last_not_of(whitespace);
    s.erase(end+1);
}

// 将 UTF-8 字符串转换为UTF16
static std::vector<uint8_t> utf8_to_utf16le(const std::string& utf8) {
    std::vector<uint8_t> utf16;
    size_t i = 0;
    while (i < utf8.size()) {
        uint32_t code_point = 0;
        if ((utf8[i] & 0x80) == 0) { // 1-byte sequence
            code_point = utf8[i++];
        } else if ((utf8[i] & 0xE0) == 0xC0) { // 2-byte sequence
            code_point = (utf8[i++] & 0x1F) << 6;
            code_point |= (utf8[i++] & 0x3F);
        } else if ((utf8[i] & 0xF0) == 0xE0) { // 3-byte sequence
            code_point = (utf8[i++] & 0x0F) << 12;
            code_point |= (utf8[i++] & 0x3F) << 6;
            code_point |= (utf8[i++] & 0x3F);
        } else if ((utf8[i] & 0xF8) == 0xF0) { // 4-byte sequence
            code_point = (utf8[i++] & 0x07) << 18;
            code_point |= (utf8[i++] & 0x3F) << 12;
            code_point |= (utf8[i++] & 0x3F) << 6;
            code_point |= (utf8[i++] & 0x3F);
        } else {
            // Invalid UTF-8 sequence
            throw std::runtime_error("Invalid UTF-8 sequence");
        }

        // Convert code point to UTF-16LE
        if (code_point <= 0xFFFF) {
            utf16.push_back(code_point & 0xff);
            utf16.push_back(code_point >> 8 & 0xff);
        } else if (code_point <= 0x10FFFF) {
            code_point -= 0x10000;
            uint16_t tcode = 0xD800 | ((code_point >> 10) & 0x3FF);
            utf16.push_back(tcode & 0xff);
            utf16.push_back(tcode >> 8 & 0xff);
            tcode = 0xDC00 | (code_point & 0x3FF);
            utf16.push_back(tcode & 0xff);
            utf16.push_back(tcode >> 8 & 0xff);
            
        } else {
            throw std::runtime_error("Invalid code point");
        }
    }
    return utf16;
}


// 将UTF-8字符串转换为Unicode码点序列
static std::vector<char32_t> utf8_to_codepoints(const std::string &utf8str) {
    std::vector<char32_t> codepoints;
    for (size_t i = 0; i < utf8str.size();) {
        unsigned char c = utf8str[i];
        if (c <= 0x7F) { // 单字节
            codepoints.push_back(c);
            i += 1;
        } else if ((c & 0xE0) == 0xC0) { // 两字节
            if (i + 1 >= utf8str.size()) break;
            char32_t cp = ((c & 0x1F) << 6) | (utf8str[i+1] & 0x3F);
            codepoints.push_back(cp);
            i += 2;
        } else if ((c & 0xF0) == 0xE0) { // 三字节
            if (i + 2 >= utf8str.size()) break;
            char32_t cp = ((c & 0x0F) << 12) | ((utf8str[i+1] & 0x3F) << 6) | (utf8str[i+2] & 0x3F);
            codepoints.push_back(cp);
            i += 3;
        } else if ((c & 0xF8) == 0xF0) { // 四字节
            if (i + 3 >= utf8str.size()) break;
            char32_t cp = ((c & 0x07) << 18) | ((utf8str[i+1] & 0x3F) << 12)
                          | ((utf8str[i+2] & 0x3F) << 6) | (utf8str[i+3] & 0x3F);
            codepoints.push_back(cp);
            i += 4;
        } else {
            // 无效字节，跳过
            ++i;
        }
    }
    return codepoints;
}

// 将Unicode码点转换为UTF-8字符串
static std::string codepoint_to_utf8(char32_t cp) {
    std::string utf8;
    if (cp <= 0x7F) {
        utf8.push_back(static_cast<char>(cp));
    } else if (cp <= 0x7FF) {
        utf8.push_back(static_cast<char>(0xC0 | (cp >> 6)));
        utf8.push_back(static_cast<char>(0x80 | (cp & 0x3F)));
    } else if (cp <= 0xFFFF) {
        utf8.push_back(static_cast<char>(0xE0 | (cp >> 12)));
        utf8.push_back(static_cast<char>(0x80 | ((cp >> 6) & 0x3F)));
        utf8.push_back(static_cast<char>(0x80 | (cp & 0x3F)));
    } else if (cp <= 0x10FFFF) {
        utf8.push_back(static_cast<char>(0xF0 | (cp >> 18)));
        utf8.push_back(static_cast<char>(0x80 | ((cp >> 12) & 0x3F)));
        utf8.push_back(static_cast<char>(0x80 | ((cp >> 6) & 0x3F)));
        utf8.push_back(static_cast<char>(0x80 | (cp & 0x3F)));
    } else {
        utf8.push_back('?');
    }
    return utf8;
}

//utf8转拼音
static std::string pinyin_utf8(const std::string &utf8str)
{
    auto codepoints = utf8_to_codepoints(utf8str);
    std::vector<std::string> result;
    for (char32_t cp : codepoints) {
        auto it = _unicode2py.find(cp);
        if (it != _unicode2py.end() && !it->second.empty()) {
            result.push_back(it->second[0]); // 取第一个拼音
        } else {
            result.push_back(codepoint_to_utf8(cp));
        }
    }

    // 拼接结果
    std::string output;
    for (auto & str : result) 
    {
        output += "'" + str;
    }
    return output;
}




#ifdef __cplusplus
extern "C"
{
#endif

SCELMAKER_API
int scel_init(const char *pydata, const char *unicode2py)
{
    //init _pydata
    std::ifstream in(pydata, std::ios::binary|std::ios::in);
    if(!in.is_open())
    {
        std::cerr << "Failed to open pinyin data file: " << pydata << std::endl;
        return -1;
    }
    in.seekg(0, std::ios::end);
    _pydata_size = in.tellg();
    _pydata = new char[_pydata_size];
    in.seekg(0, std::ios::beg);
    in.read(_pydata, _pydata_size);
    in.close();

    //init _unicode2py: 格式示例：U+XXXX: zhang,chang # 长
    in.open(unicode2py);
    if(!in.is_open())
    {
        std::cerr << "Failed to open unicode2py file: " << unicode2py << std::endl;
        return -2;
    }
    std::string line;
    while (std::getline(in, line)) {
        if (line.empty() || line[0] == '#') continue;
        size_t colon_pos = line.find(':');
        if (colon_pos == std::string::npos) continue;
        
        std::string codepoint_str = line.substr(0, colon_pos);
        if (codepoint_str.size() < 3 || codepoint_str.substr(0, 2) != "U+") continue;
        
        char32_t codepoint;
        try {
            codepoint = std::stoul(codepoint_str.substr(2), nullptr, 16);
        } catch (...) {
            continue;
        }
        
        std::string pinyin_str = line.substr(colon_pos + 1);
        pinyin_str.erase(0, pinyin_str.find_first_not_of(" \t"));
        pinyin_str.erase(pinyin_str.find_first_of(" \t"));
        
        std::vector<std::string> pinyins;
        std::istringstream iss(pinyin_str);
        std::string pinyin;
        while (std::getline(iss, pinyin, ',')) {
            if (!pinyin.empty()) {
                pinyins.push_back(pinyin);
            }
        }
        
        if (!pinyins.empty()) {
            _unicode2py[codepoint] = pinyins;
        }
    }
    

    //init _py2idx 
    auto & pylist = get_pinyin_list();
    for(auto i =0; i<pylist.size(); i++)
    {
        _py2idx[pylist[i]] = i;
    }

    return 0;
}


// 关键函数
// input: 明文搜狗细胞词库路径，目前仅支持utf8编码(无BOM)
// output: 打包后输出的搜狗细胞词库路径
// pinyin: 二进制拼音数据pinyin.bin的完整路径
// @ret: 返回0表示打包成功, 否则打包失败
SCELMAKER_API
int scel_make(const char *input, const char *output)
{
    // 生成输出文件名
    std::string inputPath(input);
    std::string pathName = inputPath.substr(0, inputPath.find_last_of('.'));
    auto tpos = pathName.find_last_of('/');
    if(tpos == pathName.npos)
    {
        tpos = inputPath.find_last_of('\\');
    }
    std::string fileName = tpos == pathName.npos ? pathName : pathName.substr(tpos);
    std::string outputName(output);
    

    // 打开输出文件
    std::ofstream out(outputName);
    if (!out.is_open())
    {
        std::cerr << "Failed to create output file: " << outputName << std::endl;
        return -2;
    }

    // 写入文件头
    std::vector<uint8_t>header(0x120, 0);
    header[0] = 0x40;
    header[1] = 0x15;
    header[4] = 0xD2;
    header[5] = 0x6D;
    header[6] = 0x53;
    header[7] = 0x01;
    header[8] = 1;

    // 生成随机数并写入
    uint32_t randNum = static_cast<uint32_t>(std::rand());
    //注意这里必须保证randStr是utf8编码
    std::string randStr = "L" + std::to_string(static_cast<uint16_t>(randNum));
    auto su16 = utf8_to_utf16le(randStr);
    auto idx = 0x1C;
    for(auto u16 : su16)
    {
        header[idx++] = u16;
    }

    // 写入时间戳
    uint32_t now = static_cast<uint32_t>(std::time(nullptr));
    *reinterpret_cast<uint32_t*>(&header[0x11C]) = to_little_endian(now);

    out.write(reinterpret_cast<char*>(header.data()), header.size());

    // 填充 0 直到 0x1540
    for(int i=0x120; i<0x1540; i++)
    {
        out.put(0);
    }

    //写入pinyin.bin
    out.write(_pydata, _pydata_size);

    // 读取输入文件内容
    std::string line; //注意必须是UTF8编码
    uint32_t cSize = 0, cCount = 0, wSize = 0, wCount = 0;
    //std::map<std::string, bool> codeMap;
    std::vector<std::string> examples;

    // 打开输入文件
    std::ifstream in(input);
    if (!in.is_open())
    {
        std::cerr << "Failed to open input file: " << input << std::endl;
        return -1;
    }

    while (std::getline(in, line)) {
        trimspace(line);
        size_t spacePos = line.find(' ');
        std::string code, word;
        if (spacePos == std::string::npos)
        {
            word = line;
            code = pinyin_utf8(word).erase(0,1);
        }
        else
        {
            code = line.substr(1, spacePos-1);
            word = line.substr(spacePos + 1);
        }


        // 处理拼音
        std::vector<std::string> sylls;
        size_t start = 0;
        while ((start = code.find("'")) != std::string::npos) {
            sylls.push_back(code.substr(0, start));
            if(_py2idx.find(sylls.back()) == _py2idx.end())
            {
                goto UNKNOWN_SYLL;
            }
            code = code.substr(start + 1);
        }
        if(_py2idx.find(code) == _py2idx.end())
        {
UNKNOWN_SYLL:
            printf("Warning unknow syll [%s] in line [%s]\n", code.c_str(), line.c_str());
            continue;
        }
        sylls.push_back(code);

        // 写入词条标记
        out.put(0x01);
        out.put(0x00);

        // 写入拼音长度
        uint16_t syllCount = static_cast<uint16_t>(sylls.size() * 2);
        //out.write(reinterpret_cast<char*>(&syllCount), sizeof(syllCount));
        out.put(syllCount & 0xff);
        out.put(syllCount >> 8 & 0xff);

        // 写入拼音索引
        for (const auto& syll : sylls) {
            uint16_t idx = _py2idx.at(syll);
            out.put(idx & 0xff);
            out.put(idx >> 8 & 0xff);
        }

        cSize += sylls.size() * 2 + 2;
        cCount++;

        // 添加示例词
        if (examples.size() < 6) {
            examples.push_back(word);
        }

        // 写入词条
        su16 = utf8_to_utf16le(word);
        uint16_t wordLength = su16.size();
        out.put(wordLength & 0xff);
        out.put(wordLength >> 8 & 0xff);
        out.write((const char *)su16.data(), su16.size());

        // 写入固定尾部
        std::vector<uint8_t> tail = {0x0A, 0x00, 0x2D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
        out.write(reinterpret_cast<char*>(tail.data()), tail.size());

        wSize += su16.size() + 2;
        wCount++;
    }
    in.close();
    //刷新写入
    out.flush();
    out.close();

    // 计算校验和
    in.open(outputName);
    in.seekg(0x1540);
    auto checksum = CheckSumStream(in);
    in.close();

    // 回写关键信息
    std::ofstream out2(outputName, std::ios::in | std::ios::out);
    out2.seekp(0xC);
    for (auto v : checksum)
    {
        v = to_little_endian(v);
        out2.write(reinterpret_cast<char*>(&v), sizeof(v));
    }

    // 写入词条统计信息
    out2.seekp(0x120);
    uint32_t tv = to_little_endian(cCount);
    out2.write(reinterpret_cast<char*>(&tv), sizeof(tv));
    tv = to_little_endian(wCount);
    out2.write(reinterpret_cast<char*>(&tv), sizeof(tv));
    tv = to_little_endian(cSize);
    out2.write(reinterpret_cast<char*>(&tv), sizeof(tv));
    tv = to_little_endian(wSize);
    out2.write(reinterpret_cast<char*>(&tv), sizeof(tv));

    // 写入文件名等信息
    //out2.seekp(0x130);
    su16 = utf8_to_utf16le(fileName);
    out2.write((const char *)su16.data(), su16.size());

    out2.seekp(0x338);
    su16 = utf8_to_utf16le("本地");
    out2.write((const char *)su16.data(), su16.size());

    out2.seekp(0x540);
    su16 = utf8_to_utf16le("由 scel-maker 生成的细胞词库");
    out2.write((const char *)su16.data(), su16.size());

    std::string exampleStr;
    for (const auto& ex : examples) {
        exampleStr += "   " + ex;
    }
    if(!exampleStr.empty())
    {
        exampleStr = exampleStr.substr(3);
    }

    out2.seekp(0xD40);
    su16 = utf8_to_utf16le(exampleStr);
    out2.write((const char *)su16.data(), su16.size());
    std::cout << "SCEL file generated: " << outputName << std::endl;
    return 0;
}

SCELMAKER_API
void scel_destroy(void)
{
    delete [] _pydata;
    _pydata_size = 0;
    _py2idx.clear();
    _unicode2py.clear();
}

#ifdef __cplusplus
}
#endif



